package simpledb.buffer;

import simpledb.file.*;

/**
 * The publicly-accessible buffer manager. A buffer manager wraps a basic buffer
 * manager, and provides the same methods. The difference is that the methods
 * {@link #pin(Block) pin} and {@link #pinNew(String, PageFormatter) pinNew}
 * will never return null. If no buffers are currently available, then the
 * calling thread will be placed on a waiting list. The waiting threads are
 * removed from the list when a buffer becomes available. If a thread has been
 * waiting for a buffer for an excessive amount of time (currently, 10 seconds)
 * then a {@link BufferAbortException} is thrown.<br>
 * 可公开访问的缓冲区管理器。 缓冲管理器包装一个基本的缓冲管理器，并提供相同的方法。 区别在于方法{@link #pin（Block）pin}和
 * {@link #pinNew（String，PageFormatter）pinNew}永远不会返回null。
 * 如果当前没有缓冲区可用，那么调用线程将被放置在等待列表上。 当缓冲区变为可用时，等待的线程从列表中删除。
 * 如果一个线程已经等待了一个缓冲区过多的时间（目前，10秒），则会抛出一个{@link BufferAbortException}。
 * 
 * @author Edward Sciore
 */
public class BufferMgr {
  private static final long MAX_TIME = 10000; // 10 seconds
  private BasicBufferMgr bufferMgr;

  /**
   * Creates a new buffer manager having the specified number of buffers. This
   * constructor depends on both the {@link FileMgr} and
   * {@link simpledb.log.LogMgr LogMgr} objects that it gets from the class
   * {@link simpledb.server.SimpleDB}. Those objects are created during system
   * initialization. Thus this constructor cannot be called until
   * {@link simpledb.server.SimpleDB#initFileAndLogMgr(String)} or is called
   * first.<br>
   * 创建具有指定数量的缓冲区的新缓冲区管理器。 此构造函数取决于{@link simpledb.server.SimpleDB}类中的
   * {@link FileMgr}和{@link simpledb.log.LogMgr LogMgr}对象。 这些对象在系统初始化期间创建。
   * 因此，这个构造函数不能被调用，直到{@link simpledb.server.SimpleDB#initFileAndLogMgr(String)}
   * 被首先调用。
   * 
   * @param numbuffers the number of buffer slots to allocate 要分配的缓冲槽数
   */
  public BufferMgr(int numbuffers) {
    bufferMgr = new BasicBufferMgr(numbuffers);
  }

  /**
   * Pins a buffer to the specified block, potentially waiting until a buffer
   * becomes available. If no buffer becomes available within a fixed time
   * period, then a {@link BufferAbortException} is thrown. <br>
   * 将缓冲区定位到指定的块，可能等待直到缓冲区变为可用。 如果在固定时间段内没有可用的缓冲区，则抛出
   * {@link BufferAbortException}。
   * 
   * @param blk a reference to a disk block 对磁盘块的引用
   * @return the buffer pinned to that block 固定到该块的缓冲区
   */
  public synchronized Buffer pin(Block blk) {
    try {
      long timestamp = System.currentTimeMillis();
      Buffer buff = bufferMgr.pin(blk);
      while (buff == null && !waitingTooLong(timestamp)) {
        wait(MAX_TIME);
        buff = bufferMgr.pin(blk);
      }
      if (buff == null)
        throw new BufferAbortException();
      return buff;
    } catch (InterruptedException e) {
      throw new BufferAbortException();
    }
  }

  /**
   * Pins a buffer to a new block in the specified file, potentially waiting
   * until a buffer becomes available. If no buffer becomes available within a
   * fixed time period, then a {@link BufferAbortException} is thrown.<br>
   * 将缓冲区引入指定文件中的新块，可能会等到缓冲区变为可用状态。 如果在固定时间段内没有可用的缓冲区，则抛出
   * {@link BufferAbortException}。
   * 
   * @param filename the name of the file 文件的名称
   * @param fmtr the formatter used to initialize the page 用于初始化Page的PageFormatter
   * @return the buffer pinned to that block 缓冲区固定到该块
   */
  public synchronized Buffer pinNew(String filename, PageFormatter fmtr) {
    try {
      long timestamp = System.currentTimeMillis();
      Buffer buff = bufferMgr.pinNew(filename, fmtr);
      while (buff == null && !waitingTooLong(timestamp)) {
        wait(MAX_TIME);
        buff = bufferMgr.pinNew(filename, fmtr);
      }
      if (buff == null)
        throw new BufferAbortException();
      return buff;
    } catch (InterruptedException e) {
      throw new BufferAbortException();
    }
  }

  /**
   * Unpins the specified buffer. If the buffer's pin count becomes 0, then the
   * threads on the wait list are notified.<br>
   * 取消指定的缓冲区Unpins。 如果缓冲区的引脚计数变为0，则通知等待列表上的线程。
   * 
   * @param buff the buffer to be unpinned
   */
  public synchronized void unpin(Buffer buff) {
    bufferMgr.unpin(buff);
    if (!buff.isPinned())
      notifyAll();
  }

  /**
   * Flushes the dirty buffers modified by the specified transaction.<br>
   * 刷新由指定事务修改的脏缓冲区
   * 
   * @param txnum the transaction's id number 事务的ID号
   */
  public void flushAll(int txnum) {
    bufferMgr.flushAll(txnum);
  }

  /**
   * Returns the number of available (ie unpinned) buffers.
   * <br>返回可用（即未固定）缓冲区的数量。
   * @return the number of available buffers 可用缓冲区数
   */
  public int available() {
    return bufferMgr.available();
  }

  private boolean waitingTooLong(long starttime) {
    return System.currentTimeMillis() - starttime > MAX_TIME;
  }
}
